/* Copyright (C) 2004 - 2006  db4objects Inc.  http://www.db4o.com

This file is part of the db4o open source object database.

db4o is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */
namespace Db4objects.Db4o.Internal.Handlers
{
	/// <summary>n-dimensional array</summary>
	/// <exclude></exclude>
	public sealed class MultidimensionalArrayHandler : Db4objects.Db4o.Internal.Handlers.ArrayHandler
	{
		public MultidimensionalArrayHandler(Db4objects.Db4o.Internal.ObjectContainerBase 
			stream, Db4objects.Db4o.Internal.ITypeHandler4 a_handler, bool a_isPrimitive) : 
			base(stream, a_handler, a_isPrimitive)
		{
		}

		public sealed override object[] AllElements(object a_array)
		{
			int[] dim = _reflectArray.Dimensions(a_array);
			object[] flat = new object[ElementCount(dim)];
			_reflectArray.Flatten(a_array, dim, 0, flat, 0);
			return flat;
		}

		public int ElementCount(Db4objects.Db4o.Internal.Transaction a_trans, Db4objects.Db4o.Internal.Buffer
			 a_bytes)
		{
			return ElementCount(ReadDimensions(a_trans, a_bytes, new Db4objects.Db4o.Reflect.IReflectClass
				[1]));
		}

		private int ElementCount(int[] a_dim)
		{
			int elements = a_dim[0];
			for (int i = 1; i < a_dim.Length; i++)
			{
				elements = elements * a_dim[i];
			}
			return elements;
		}

		public sealed override byte Identifier()
		{
			return Db4objects.Db4o.Internal.Const4.YAPARRAYN;
		}

		public sealed override int ObjectLength(object a_object)
		{
			int[] dim = _reflectArray.Dimensions(a_object);
			return Db4objects.Db4o.Internal.Const4.OBJECT_LENGTH + (Db4objects.Db4o.Internal.Const4
				.INT_LENGTH * (2 + dim.Length)) + (ElementCount(dim) * i_handler.LinkLength());
		}

		public override int OwnLength(object obj)
		{
			int[] dim = _reflectArray.Dimensions(obj);
			return Db4objects.Db4o.Internal.Const4.OBJECT_LENGTH + (Db4objects.Db4o.Internal.Const4
				.INT_LENGTH * (2 + dim.Length));
		}

		public sealed override object Read1(Db4objects.Db4o.Internal.Marshall.MarshallerFamily
			 mf, Db4objects.Db4o.Internal.StatefulBuffer reader)
		{
			object[] ret = new object[1];
			int[] dim = Read1Create(reader.GetTransaction(), reader, ret);
			if (ret[0] != null)
			{
				object[] objects = new object[ElementCount(dim)];
				for (int i = 0; i < objects.Length; i++)
				{
					objects[i] = i_handler.Read(mf, reader, true);
				}
				_reflectArray.Shape(objects, 0, ret[0], dim, 0);
			}
			return ret[0];
		}

		protected override int ReadElementsDefrag(Db4objects.Db4o.Internal.ReaderPair readers
			)
		{
			int numDimensions = base.ReadElementsDefrag(readers);
			int[] dimensions = new int[numDimensions];
			for (int i = 0; i < numDimensions; i++)
			{
				dimensions[i] = readers.ReadInt();
			}
			return ElementCount(dimensions);
		}

		public sealed override void Read1Candidates(Db4objects.Db4o.Internal.Marshall.MarshallerFamily
			 mf, Db4objects.Db4o.Internal.Buffer reader, Db4objects.Db4o.Internal.Query.Processor.QCandidates
			 candidates)
		{
			object[] ret = new object[1];
			int[] dim = Read1Create(candidates.i_trans, reader, ret);
			if (ret[0] != null)
			{
				int count = ElementCount(dim);
				for (int i = 0; i < count; i++)
				{
					Db4objects.Db4o.Internal.Query.Processor.QCandidate qc = i_handler.ReadSubCandidate
						(mf, reader, candidates, true);
					if (qc != null)
					{
						candidates.AddByIdentity(qc);
					}
				}
			}
		}

		public sealed override object Read1Query(Db4objects.Db4o.Internal.Transaction a_trans
			, Db4objects.Db4o.Internal.Marshall.MarshallerFamily mf, Db4objects.Db4o.Internal.Buffer
			 a_bytes)
		{
			object[] ret = new object[1];
			int[] dim = Read1Create(a_trans, a_bytes, ret);
			if (ret[0] != null)
			{
				object[] objects = new object[ElementCount(dim)];
				for (int i = 0; i < objects.Length; i++)
				{
					objects[i] = i_handler.ReadQuery(a_trans, mf, true, a_bytes, true);
				}
				_reflectArray.Shape(objects, 0, ret[0], dim, 0);
			}
			return ret[0];
		}

		private int[] Read1Create(Db4objects.Db4o.Internal.Transaction a_trans, Db4objects.Db4o.Internal.Buffer
			 a_bytes, object[] obj)
		{
			Db4objects.Db4o.Reflect.IReflectClass[] clazz = new Db4objects.Db4o.Reflect.IReflectClass
				[1];
			int[] dim = ReadDimensions(a_trans, a_bytes, clazz);
			if (i_isPrimitive)
			{
				obj[0] = a_trans.Reflector().Array().NewInstance(i_handler.PrimitiveClassReflector
					(), dim);
			}
			else
			{
				if (clazz[0] != null)
				{
					obj[0] = a_trans.Reflector().Array().NewInstance(clazz[0], dim);
				}
			}
			return dim;
		}

		private int[] ReadDimensions(Db4objects.Db4o.Internal.Transaction a_trans, Db4objects.Db4o.Internal.Buffer
			 a_bytes, Db4objects.Db4o.Reflect.IReflectClass[] clazz)
		{
			int[] dim = new int[ReadElementsAndClass(a_trans, a_bytes, clazz)];
			for (int i = 0; i < dim.Length; i++)
			{
				dim[i] = a_bytes.ReadInt();
			}
			return dim;
		}

		public sealed override void WriteNew1(object obj, Db4objects.Db4o.Internal.StatefulBuffer
			 writer)
		{
			int[] dim = _reflectArray.Dimensions(obj);
			WriteClass(obj, writer);
			writer.WriteInt(dim.Length);
			for (int i = 0; i < dim.Length; i++)
			{
				writer.WriteInt(dim[i]);
			}
			object[] objects = AllElements(obj);
			Db4objects.Db4o.Internal.Marshall.MarshallerFamily mf = Db4objects.Db4o.Internal.Marshall.MarshallerFamily
				.Current();
			for (int i = 0; i < objects.Length; i++)
			{
				i_handler.WriteNew(mf, Element(objects, i), false, writer, true, true);
			}
		}

		private object Element(object a_array, int a_position)
		{
			try
			{
				return _reflectArray.Get(a_array, a_position);
			}
			catch
			{
				return null;
			}
		}
	}
}
